package com.audaque.springboot.foshanupload.web.nettydemo.component;

import com.audaque.springboot.foshanupload.web.nettydemo.handler.*;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler;
import io.netty.handler.codec.json.JsonObjectDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * @author zgb
 * @desc ...
 * @date 2023-08-08 20:02:48
 */

@Component
public class NettyServer {

    public void start(int port) throws InterruptedException {
        // 用来接收客户端传进来的连接
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        // 用来处理已被接收的连接
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        // 创建 netty 服务
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        try {
            serverBootstrap.group(bossGroup, workerGroup)
                    //.localAddress(port)// 绑定监听端口
                    // 设置 NIO 模式
                    .channel(NioServerSocketChannel.class)
                    // 设置 tcp 缓冲区
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    // 设置发送缓冲区数据大小
                    .childOption(ChannelOption.SO_SNDBUF, 64 * 1024)
                    // 设置接收缓冲区数据大小
                    .option(ChannelOption.SO_RCVBUF, 64 * 1024)
                    // 保持长连接
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            //按顺序注册处理器，消息会进过处理器链传递
                            //以块的方式来写的处理器
                            pipeline.addLast(new ChunkedWriteHandler());
                            //websocket协议本身是基于http协议的，所以这边也要使用http解编码器
                            // HttpClient编解码器
                            pipeline.addLast(new HttpServerCodec());
                            //JSon解码器。
                            pipeline.addLast(new JsonObjectDecoder());
                            // 发送字符串的编码器。
                            pipeline.addLast(new StringEncoder());
                            // 接收到字符串的解码器。
                            pipeline.addLast(new StringDecoder());






                            // 设置最大内容长度
                            pipeline.addLast(new HttpObjectAggregator(65536));
                            // WebSocket 数据压缩扩展
                            pipeline.addLast(new WebSocketServerCompressionHandler());


                            /**
                             *  EchoInboundHandler与SimpleChannelInboundHandler不兼容，不要同时使用
                             */

                            //outboundhandler一定要放在最后一个inboundhandler之前,否则outboundhandler将不会执行到
                            // InboundHandler是按照Pipleline的加载顺序，顺序执行。
                            // OutboundHandler是按照Pipeline的加载顺序，逆序执行。
               /*             pipeline.addFirst(new EchoOutboundHandler3());
                            pipeline.addFirst(new EchoOutboundHandler2());
                            pipeline.addFirst(new EchoOutboundHandler1());

                            pipeline.addLast(new EchoInboundHandler1());
                            pipeline.addLast(new EchoInboundHandler2());
                            pipeline.addLast(new EchoInboundHandler3());*/
                            /**
                             * 注册顺序
                             * out1
                             * out2
                             * out3
                             * in1
                             * in2
                             * in3
                             */
                            /**
                             * 执行顺序:in与注册顺序相同，out与注册顺序相反
                             * in1
                             * in2
                             * in3
                             * out3
                             * out2
                             * out1
                             */

                            // 通道的初始化，数据传输过来进行拦截及执行
                            pipeline.addLast(new SimpleChannelInboundHandler1());
                            // WebSocket 握手、控制帧处理
                            pipeline.addLast(new WebSocketServerProtocolHandler("/", null, true, 65536 * 10));

                            /**
                             * 开启服务端心跳检测：默认关闭
                             * 确保IdleStateHandler在其他ChannelHandler之前注册。
                             * IdleStateHandler
                             * readerIdleTimeSeconds: 读超时。即当在指定的时间间隔内没有从 Channel 读取到数据时, 会触发一个 READER_IDLE 的 IdleStateEvent 事件。
                             * writerIdleTimeSeconds: 写超时。 即当在指定的时间间隔内没有数据写入到 Channel 时, 会触发一个 WRITER_IDLE 的 IdleStateEvent 事件。
                             * allIdleTimeSeconds: 读/写超时。 即当在指定的时间间隔内没有读或写操作时, 会触发一个 ALL_IDLE 的 IdleStateEvent 事件。
                             * IdleStateHandler的readerIdleTime参数指定超过X秒还没收到客户端的连接，
                             * 会触发IdleStateEvent事件并且交给下一个handler处理，下一个handler必须实现userEventTriggered方法处理对应事件
                             */
                            pipeline.addFirst(new IdleStateHandler(1,0,0, TimeUnit.SECONDS));

                        }
                    });
            // 绑定端口启动服务
            ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
            channelFuture.channel().closeFuture().sync();
        } finally {
            // 释放线程池资源
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }


}
